package DARCOShared.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SealedObject;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

import org.apache.commons.lang.RandomStringUtils;

public class SecurityUtilities {
	public static final String ServerPublicKey = "rO0ABXNyAD5vcmcuYm91bmN5Y2FzdGxlLmpjYWpjZS5wcm92aWRlci5hc3ltbWV0cmljLnJzYS5CQ1JTQVB1YmxpY0tleSUiag5b+myEAgACTAAHbW9kdWx1c3QAFkxqYXZhL21hdGgvQmlnSW50ZWdlcjtMAA5wdWJsaWNFeHBvbmVudHEAfgABeHBzcgAUamF2YS5tYXRoLkJpZ0ludGVnZXKM/J8fqTv7HQMABkkACGJpdENvdW50SQAJYml0TGVuZ3RoSQATZmlyc3ROb256ZXJvQnl0ZU51bUkADGxvd2VzdFNldEJpdEkABnNpZ251bVsACW1hZ25pdHVkZXQAAltCeHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhw///////////////+/////gAAAAF1cgACW0Ks8xf4BghU4AIAAHhwAAABAJqtGUeQ2wlUS2IHsYPUGlCV6rt5Tae5c05FTwoHj2zNyYywQKa3dQV0M27lxNfPdRK0deOBV9LMZzWqzJ26cF2fY+O8KoiIU7zjMhJnIK2F18WnRn4fpf86HzNCOT5yfoxyVRDM4vEOdvPXMCvSJ4KUXg4RfkAI9iNC9iBtmx6hOgO+Ygx8dpP6DNZB9YoWlOV7kzNdANUQTnfhs5XV0cUM1IExegUe2kQKZxASlhsD9Cvc/L7mQy1i0G+V9wZqsZWKncMAQel3893JE/5fIlfr3zW4TxjgznXDb4V7GCwRkawCp0DS6r3eufUcryf+Z2yfo0MuJqx2SpBYyJpa6D14c3EAfgAD///////////////+/////gAAAAF1cQB+AAcAAAADAQABeA==";


	public static KeyPair String2KeyPair(String s){
		try{
			byte[] data = Base64.decode(s);

			ByteArrayInputStream b = new ByteArrayInputStream(data);
			ObjectInputStream o = new ObjectInputStream(b);
			Object obj =o.readObject();
			return((KeyPair)obj);
		}catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static KeyPair Bin2KeyPair(byte[] data){
		try{
			ByteArrayInputStream b = new ByteArrayInputStream(data);
			ObjectInputStream o = new ObjectInputStream(b);
			Object obj =o.readObject();
			return((KeyPair)obj);
		}catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static String SerializeToString(java.io.Serializable obj){

		ByteArrayOutputStream b = new ByteArrayOutputStream();
		ObjectOutputStream o;
		try {
			o = new ObjectOutputStream(b);
			o.writeObject(obj);
			return Base64.encodeBytes(b.toByteArray());
		} catch (IOException e) {
			e.printStackTrace();
		}	
		return  null;
	}

	public static byte[] SerializeToBin(java.io.Serializable obj){

		ByteArrayOutputStream b = new ByteArrayOutputStream();
		ObjectOutputStream o;
		try {
			o = new ObjectOutputStream(b);
			o.writeObject(obj);
			return b.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
		}	
		return  null;
	}


	public static RSAPrivateCrtKey String2PrivateKey(String s){
		try{
			byte[] data = Base64.decode(s);

			ByteArrayInputStream b = new ByteArrayInputStream(data);
			ObjectInputStream o = new ObjectInputStream(b);
			Object obj =o.readObject();
			return((RSAPrivateCrtKey)obj);
		}catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static RSAPublicKey String2PublicKey(String s){
		try{
			byte[] data = Base64.decode(s);

			ByteArrayInputStream b = new ByteArrayInputStream(data);
			ObjectInputStream o = new ObjectInputStream(b);
			Object obj =o.readObject();
			return((RSAPublicKey)obj);
		}catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static RSAPrivateCrtKey Bin2PrivateKey(byte[] data){
		try{
			ByteArrayInputStream b = new ByteArrayInputStream(data);
			ObjectInputStream o = new ObjectInputStream(b);
			Object obj =o.readObject();
			return((RSAPrivateCrtKey)obj);
		}catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static RSAPublicKey Bin2PublicKey(byte[] data){
		try{
			ByteArrayInputStream b = new ByteArrayInputStream(data);
			ObjectInputStream o = new ObjectInputStream(b);
			Object obj =o.readObject();
			return((RSAPublicKey)obj);
		}catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static RSAPublicKey getServerPublicKey(){
		return SecurityUtilities.String2PublicKey(SecurityUtilities.ServerPublicKey);
	}

	public static KeyPair generateRSAKeypair(int length){
		try {
			KeyPairGenerator kp = KeyPairGenerator.getInstance("RSA", "BC");
			kp.initialize(length, new SecureRandom());
			return kp.generateKeyPair();
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}


	public static byte[] encode(Key publicKey, byte[] data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException {

		Cipher cipher = Cipher.getInstance("RSA", "BC"); // create conversion processing object
		cipher.init(Cipher.ENCRYPT_MODE, publicKey); // initialize object's mode and key

		byte[] encryptedByteData = cipher.doFinal(data); // use object for encryption

		return encryptedByteData; // convert encrypted byte array to string and return it

	}

	public static byte[] decode(Key privateKey, byte[] data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException {

		Cipher cipher = Cipher.getInstance("RSA", "BC"); // create conversion processing object
		cipher.init(Cipher.DECRYPT_MODE, privateKey); // initialize object's mode and key

		byte[] decryptedByteData = cipher.doFinal(data); // use object for decryption

		return decryptedByteData; // convert decrypted byte array to string and return it

	}

	public static byte[] PBEEncrypt(byte[] salt, String password, byte[] data){
		try {
			PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt,20);
			PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
			SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBEWithSHA1AndDES", "BC");
			SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec);

			Cipher cipher = Cipher.getInstance("PBEWithSHA1AndDES", "BC");
			cipher.init(Cipher.ENCRYPT_MODE,secretKey,pbeParamSpec);

			return cipher.doFinal(data);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public static byte[] PBEDecrypt(byte[] salt, String password, byte[] data){
		try{
			PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 20);
			PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
			SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBEWithSHA1AndDES", "BC");
			SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec);

			Cipher cipher = Cipher.getInstance("PBEWithSHA1AndDES", "BC");
			cipher.init(Cipher.DECRYPT_MODE,secretKey,pbeParamSpec);
			return cipher.doFinal(data);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}


	public static String getRandomString(int size)
	{
		return RandomStringUtils.randomAscii(size);
	}

	public static byte[] signData(byte[] input, PrivateKey privateKey){
		try {
			Signature instance = Signature.getInstance("SHA1withRSA", "BC");
			instance.initSign(privateKey);
			instance.update(input);
			byte[] sig =instance.sign(); 
			return sig;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} 
	}

	public static boolean verifySignature(byte[] data, byte[] signature, PublicKey publicKey){
		try{
			Signature sig = Signature.getInstance("SHA1withRSA", "BC");
			sig.initVerify(publicKey);
			sig.update(data);
			return sig.verify(signature);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		} 
	}
}
